package caclient


import (
	"encoding/json"
	"fmt"
	"gitee.com/wayjin/fabric-extension/cryptoutil"
	fabricmsp "github.com/hyperledger/fabric/msp"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"path/filepath"
)

type IdentityType string

const (
	ID_PEER IdentityType = "peer"
	ID_ORDERER = "orderer"
	ID_USER = "client"
	ID_ADMIN = "admin"
)

const (
	CLIENT = iota
	ORDERER
	PEER
	ADMIN
)

const (
	CLIENTOU  = "client"
	PEEROU    = "peer"
	ADMINOU   = "admin"
	ORDEREROU = "orderer"
)

var nodeOUMap = map[int]string{
	CLIENT:  CLIENTOU,
	PEER:    PEEROU,
	ADMIN:   ADMINOU,
	ORDERER: ORDEREROU,
}

type IdentityCryptoCert struct {
	typ  string
	nodeOUs bool
	admin cryptoutil.KeyStore
	mspcert cryptoutil.KeyStore
	tlscert cryptoutil.KeyStore
}

func (idcert IdentityCryptoCert) SaveToLocal(pathDir string) error {
	cacertDir := filepath.Join(pathDir, "msp/cacerts")
	signcertDir := filepath.Join(pathDir, "msp/signcerts")
	keycertDir := filepath.Join(pathDir, "msp/keystore")
	admincertDir := filepath.Join(pathDir, "msp/admincerts")
	tlscacertDir := filepath.Join(pathDir, "msp/tlscacerts")
	prkKey := cryptoutil.NewPrivateKey(idcert.mspcert.GetPrivateKey())
	keyBytes, err := prkKey.GetBytes(nil)
	if err != nil {
		return err
	}
	writeFile(cacertDir, "ca-cert.pem", idcert.mspcert.GetCACert())
	writeFile(signcertDir, "cert.pem", idcert.mspcert.GetSignCert())
	//fmt.Println("keyCert: ", keycertDir, "filename:", prkKey.FileName())
	writeFile(keycertDir, prkKey.FileName(), keyBytes)

	if idcert.admin != nil {
		writeFile(admincertDir, "admin-cert.pem", idcert.admin.GetSignCert())
	}

	if idcert.tlscert != nil {
		writeFile(tlscacertDir, "tlsca-cert.pem", idcert.tlscert.GetCACert())
		writeFile(filepath.Join(pathDir, "tls"), "ca.crt", idcert.tlscert.GetCACert())
		tlsPrkKeyBytes, _ := cryptoutil.NewPrivateKey(idcert.mspcert.GetPrivateKey()).GetBytes(nil)
		if idcert.typ == "peer" || idcert.typ == "orderer" {
			writeFile(filepath.Join(pathDir, "tls"), "server.crt", idcert.tlscert.GetSignCert())
			writeFile(filepath.Join(pathDir, "tls"), "server.key", tlsPrkKeyBytes)
		} else {
			writeFile(filepath.Join(pathDir, "tls"), "client.crt", idcert.tlscert.GetSignCert())
			writeFile(filepath.Join(pathDir, "tls"), "client.key", tlsPrkKeyBytes)
		}
	}
	if idcert.typ == "admin" {
		orgBaseDir := filepath.Dir(filepath.Dir(pathDir))
		writeFile(filepath.Join(orgBaseDir, "msp/cacerts"), "ca-cert.pem", idcert.mspcert.GetCACert())
		writeFile(filepath.Join(orgBaseDir, "msp/admincerts"), "admin-cert.pem", idcert.mspcert.GetSignCert())
		writeFile(filepath.Join(orgBaseDir, "msp/tlscacerts"), "tlsca-cert.pem", idcert.tlscert.GetCACert())
		if idcert.nodeOUs {
			exportConfig(filepath.Join(orgBaseDir, "msp"), true, "cacerts/ca-cert.pem")
		}
	}
	if idcert.nodeOUs {
		exportConfig(filepath.Join(pathDir, "msp"), true, "cacerts/ca-cert.pem")
	}
	return nil
}

func (idcert IdentityCryptoCert) GetMSPCert() cryptoutil.KeyStore {
	return idcert.mspcert
}

func (idcert IdentityCryptoCert) Wallet() (Wallet, error) {
	var card idcard
	if idcert.typ != ID_USER {
		return nil, fmt.Errorf("only support client type user")
	}
	card.Cert = idcert.mspcert.GetSignCert()
	prk := cryptoutil.NewPrivateKey(idcert.mspcert.GetPrivateKey())
	prkBytes, err := prk.GetBytes(nil)
	if err != nil {
		return nil, err
	}
	card.Key = prkBytes
	card.TlsCert = idcert.tlscert.GetCACert()
	return &card, nil
}

func exportConfig(path string, enable bool, caFile string) error {
	var config = &fabricmsp.Configuration{
		NodeOUs: &fabricmsp.NodeOUs{
			Enable: enable,
			ClientOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
				Certificate:                  caFile,
				OrganizationalUnitIdentifier: CLIENTOU,
			},
			PeerOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
				Certificate:                  caFile,
				OrganizationalUnitIdentifier: PEEROU,
			},
			AdminOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
				Certificate:                  caFile,
				OrganizationalUnitIdentifier: ADMINOU,
			},
			OrdererOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
				Certificate:                  caFile,
				OrganizationalUnitIdentifier: ORDEREROU,
			},
		},
	}

	data, err := yaml.Marshal(config)
	if err != nil {
		return err
	}
	writeFile(path, "config.yaml", data)
	return nil
}


func writeFile(pathDir, filename string, buffer []byte) {
	_, err := os.Stat(pathDir)
	if os.IsNotExist(err) {
		os.MkdirAll(pathDir, 0755)
	}
	ioutil.WriteFile(filepath.Join(pathDir, filename), buffer, 0644)
}

type Wallet interface {
	GetCert() []byte
	GetKey() []byte
	GetTlsCert() []byte
	GetMspID() string
}

type idcard struct {
	Cert  []byte  `json:"signcert"`
	Key  []byte  `json:"key"`
	TlsCert []byte  `json:"tlsrootcert"`
	MspID  string  `json:"mspid"`
	Version string  `json:"version"`
	CANAme string `json:"caname"`
}

func (w *idcard) Marshal(pwd []byte) ([]byte, error) {
	return json.Marshal(w)
}

func (card idcard) GetCert() []byte {
	return card.Cert
}

func (card idcard) GetTlsCert() []byte {
	return card.TlsCert
}

func (card idcard) GetKey() []byte {
	return card.Key
}

func (card idcard) GetMspID() string {
	return card.MspID
}


func DecodeWallet(data []byte) (Wallet, error) {
	var card idcard
	err := json.Unmarshal(data, &card)
	if err != nil {
		return nil, err
	}
	return &card, nil
}